#ifndef _SINGLE_
#include <iostream>
#include <fstream>
#include <string>
#endif

bool xboard=false;
bool ran_move=false;
bool idle=false;
bool keep_playing=false;
int time_left=1;
bool debug = false;
bool post = false;
bool has_offered_draw = false;

/// A custom output stream.
/**
 * It is difficult to write a good output stream.
 * tie-ing my_ostream to cin is unnecessary in this case.
 */
class my_ostream{
  std::ofstream fout;
  std::ostream& out;
  template<typename Tp>
  friend my_ostream& operator << (my_ostream& os, Tp value) {
    os.out <<value;
    os.fout <<value;
    return os;
  }
public:
  my_ostream(const char* fname,std::ostream& outt):fout(fname),out(outt) {}
  my_ostream& flush() {
    fout.flush();
    out.flush();
    return *this;
  }
  my_ostream& endl() {
    fout<<std::endl;
    out<<std::endl;
    return *this;
  }
  void print(const std::string& str) {
    fout<<str<<std::endl;
  }
} mout("xb.out", std::cout);

namespace std {
  my_ostream& flush(my_ostream& ms) {
    return ms.flush();
  }
  my_ostream& endl(my_ostream& ms) {
    return ms.endl();
  }
}

my_ostream& operator << (my_ostream& os, my_ostream& func(my_ostream&)) {
  return func(os);
}

#include "brain.h"
colour Fischer=BLACK;

int Loop() {
  while(true) {
    if(not idle) {
      do
      if(keep_playing or En.P.A.to_move==Fischer) {

	move_list l;
	En.get_moves(l);
	if(l.empty()) { idle=true; continue; }
	l.clear();

	move M(find_best());
	
	En.make_move(M);
	if(not xboard)
	  mout <<En<<std::endl;
	else {
          // if (t_bscore < -1000) {
          //  mout << "offer draw" << std::endl;
          // }
	  // mout <<En.no_of_moves()<<". ... "<<M<<'\n';
	  // mout <<"My Move is: "<<M<<std::endl;
          mout << "move " << M << std::endl;
	  En.get_moves(l);
	  if(l.empty()) {
	    idle=true;
	    if(Fischer==WHITE)
	      mout <<"1-0 { Fischer wins as white }"<<std::endl;
	    else
	      mout <<"0-1 { Fischer wins as black }"<<std::endl;
	    En.reset();
	    hash_store.clear();
	  }
	}
	
      } while(keep_playing);
    }

    if(not xboard)
      std::cout <<"Fischer: "<<std::flush;

    std::string cmd;
    std::cin>>cmd;
    mout.print(cmd);
    if(cmd=="board")
      mout <<En;
    else if(cmd=="quit")
      return 0;
    else if(cmd=="reset") {
      En.reset();
      hash_store.clear();
    }
    else if(cmd=="warranty")
      mout <<"\nFischer v1.00\n Copyright (C) 2005 Atul S. Vasu\n\n"
		<<"This program is a free software; you can redistribute it and/or modify\n"
		<<"under the terms of the GNU General Public License as published by\n"
		<<"the Free Software Foundation; either version 2 of the License, or\n"
		<<"(at your option) any later version.\n\n"
		<<"This program is distributed in the hope that it will be useful,\n"
		<<"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
		<<"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
		<<"GNU General Public License for more details.\n\n"
		<<"You should have received a copy of the GNU General Public License\n"
		<<"along with this program. If not, write to the Free Software\n"
		<<"Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"
		<<std::endl;
    else if(cmd=="xboard")
      xboard=true;
    else if(cmd=="protover") {
      int ver; std::cin >>ver;
      mout <<"feature setboard=0 analyse=0 ping=0 draw=1 sigint=0 "
		<<"variants=\"normal\" myname=\"Fischer v1.00\" done=1"
		<<std::endl;
    }
    else if(cmd == "debug") {
      debug = !debug;
    }
    else if(cmd == "hint") {
      mout << find_best();
    }
    else if(cmd=="accepted") {
      char ch;
      while(std::cin.get(ch))
	if(ch=='\n') break;
    }
    else if(cmd=="rejected") {
	char ch;
	while(std::cin.get(ch))
	  if(ch=='\n') break;
    }
    else if (cmd == "nopost") {
      post = false;
    }
    else if (cmd == "post") {
      post = true;
    }
    else if(cmd=="analyze") continue;
    else if(cmd=="random") {
      // ran_move=true;
    }
    else if(cmd=="best") ran_move=false;
    else if(cmd=="level") {
      std::string TCMove, TCTime, TCinc;
      std::cin >>TCMove>>TCTime>>TCinc;
    }
    else if(cmd=="hard") continue;
    else if(cmd=="force") {
      //      idle=true;
      ;//What to do here is a mystery.
    }
    else if(cmd=="time") {
      std::cin >>time_left;
    }
    else if(cmd=="otim") {
      int to; std::cin >>to;
    }
    else if(cmd=="undo") {
      idle=true;
      En.undo_move();
    }
    else if(cmd=="black")
      Fischer = BLACK;
    else if(cmd=="white")
      Fischer = WHITE;
    else if(cmd=="go") {
      //      Fischer = En.P.A.to_move;
      idle=false;
    }
    else if(cmd=="new") {
      En.reset();
      hash_store.clear();
    }
    else if(cmd=="depth" or cmd=="sd") {
      std::cin>>depth;
      mout <<"Search to a depth of "<<depth<<std::endl;
    }
    else if(cmd=="eval") {
      mout <<evaluate()<<std::endl;
    }
    else if(cmd=="score") {
      mout <<t_bscore<<std::endl;
    }
    else if(cmd=="both") {
      keep_playing=true;  	
      idle=false;
    } else if (cmd == "draw") {
      // do nothing.
    }
    else if(cmd=="computer") continue;
    else if(cmd=="human") continue;
    else if(cmd=="help")
      mout <<"go\t\tAccepts current player as Computer and other one as human\n"
		<<"new\t\tStarts a new game\n"
		<<"board\t\tDisplays board\n"
		<<"xboard\t\tTurns to XBoard mode.\n"
		<<"undo\t\tUndoes the last move\n"
		<<"quit\t\tQuits the program\n"
		<<"depth n\t\tSets depth to n\n"
		<<"Move is entered like a7b8=q\n"<<std::endl;
    else if(cmd=="result") {
      char ch;
      while(std::cin.get(ch)) if(ch=='\n') break;
    } else if(cmd=="remove") {
      En.undo_move();
      En.undo_move();
    }else {
      // Not a known command, it should be a move.
      move M(cmd,En.P.A.to_move);
      if(En.is_valid(M)) {
	En.make_move(M);
	if(not xboard)
	  mout <<En;
	else {
	  // mout <<En.no_of_moves()<<". "<<M<<std::endl;
	}
      }
      else {
	if(not xboard) {
	  mout <<"The valid moves are: \n";
	  move_list all_moves;
	  En.get_moves(all_moves);
	  for(move_list::iterator it=all_moves.begin();it!=all_moves.end();++it)
	    mout <<*it<<'\t';
	  mout <<std::endl;
	}
	mout <<"Invalid Move: "<<M<<std::endl;	
      }
    }
  }
}

int main(int argc, char* argv[]) {
  std::srand(std::time(0));

  for(int i=1;i<argc;i++)
    if(std::string("--xboard")==argv[i])
      xboard=true;

  if(not xboard) {
    mout <<"Fischer v1.00, Copyright (C) 2005 Atul S. Vasu\n";
    mout <<"Fischer comes with ABSOLUTELY NO WARRANTY; for details\n"
	      <<"type `warranty'.\n"<<std::endl;
    mout <<"Type help for online-help.\n"<<std::endl;
  }
  else
    mout <<"Fischer v1.00"<<std::endl;

  return Loop();
}
